/*
 * Copyright (C) 2009 M. Hautle.
 * 
 * This file is part of TrackedBean.
 * 
 * TrackedBean is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option) any later version.
 * 
 * TrackedBean is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License along with TrackedBean. If not, see <http://www.gnu.org/licenses/>.
 */
package ch.trackedbean.copier.engine;

import java.util.*;
import ch.trackedbean.copier.*;

/**
 * Default implementation of {@link IMappingContext}.
 * 
 * @author M. Hautle
 */
public class DefaultMappingContext implements IMappingContext {
	/** The mapping filter to use. */
	private final IMappingFilter filter;

	/** The cache for the get and put calls. */
	private final IdentityHashMap<Object, Object[]> cache = new IdentityHashMap<Object, Object[]>();

	/** The user configuration. Default is {@link Collections#EMPTY_MAP} cause only few calls will use this feature. */
	private Map<Object, Object> config = Collections.emptyMap();

	/**
	 * Default constructor.
	 * 
	 * @param filter The mapping filter to use
	 */
	public DefaultMappingContext(IMappingFilter filter) {
		this.filter = filter;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean copyDst2Src(IMappingHolder holder) {
		return filter.copyDst2Src(holder);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean copySrc2Dst(IMappingHolder holder) {
		return filter.copySrc2Dst(holder);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	@SuppressWarnings("unchecked")
	public <T> T getElement(Object key, Class<T> type) {
		final Object[] val = cache.get(key);
		// no value for the key
		if (val == null)
			return null;
		// lookup the entry for the specified key
		for (int i = 0; i < val.length; i++)
			if (val[i].getClass() == type)
				return (T) val[i];
		return null;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public <T> T putElement(Object key, T value) {
		final Object[] old = cache.put(key, new Object[] { value });
		if (old == null)
			return value;
		// the case that we have more than one value for the same key (which should happen rarely)
		final Object[] vals = new Object[old.length + 1];
		System.arraycopy(old, 0, vals, 0, old.length);
		vals[old.length] = value;
		cache.put(key, vals);
		return value;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	@SuppressWarnings("unchecked")
	public <T> T getValue(Object key) {
		return (T) config.get(key);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Object putValue(Object key, Object value) {
		// empty == Collections.EMPTY_MAP; so we create the real config map (this is a kind of 'lazy' creation)
		if (config.isEmpty())
			config = new HashMap<Object, Object>();
		return config.put(key, value);
	}
}
